/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * Copyright (C) 2020 KYLINOS Information Technology Co., Ltd.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */

#include "qrcodeenroll.h"
#include "ui_qrcodeenroll.h"
#include <QPixmap>
#include <QDBusUnixFileDescriptor>
#include <unistd.h>
#include <opencv2/opencv.hpp>
#include <QDBusAbstractInterface>
#include "ukcc/widgets/closebutton.h"
#include "biometricdeviceinfo.h"
#include "biometricproxy.h"
#include "servicemanager.h"
#include "giodbus.h"
#include "ukuistylehelper/ukuistylehelper.h"
#include "windowmanager/windowmanager.h"

extern void qt_blurImage(QImage &blurImage, qreal radius, bool quality, int transposed);

#define SS_DBUS_SERVICE     "org.ukui.ScreenSaver"
#define SS_DBUS_PATH        "/"
#define SS_DBUS_INTERFACE   "org.ukui.ScreenSaver"

QRCodeEnrollDialog::QRCodeEnrollDialog(QDBusInterface *service,int bioType,
                                             int deviceId, int uid, QWidget *parent) :
    QDialog(parent),
    ui(new Ui::QRCodeEnrollDialog),
    serviceInterface(service),
    type(bioType),
    deviceId(deviceId),
    uid(uid),
    ops(IDLE),
    m_hisOps(IDLE),
    opsResult(UNDEFINED),
    w_timer(nullptr)
{
    //去除窗管标题栏
    kdk::UkuiStyleHelper::self()->removeHeader(this);
    ui->setupUi(this);
    setupInit();
    connect(serviceInterface, SIGNAL(StatusChanged(int,int)),
            this, SLOT(onStatusChanged(int,int)));

    connect(serviceInterface, SIGNAL(ProcessChanged(int,QString,int,QString)),
            this, SLOT(onProcessChanged(int,QString,int,QString)));

    connect(serviceInterface, SIGNAL(FrameWritten(int)),
            this, SLOT(onFrameWritten(int)));

    ServiceManager *sm = ServiceManager::instance();
    connect(sm, &ServiceManager::serviceStatusChanged,
            this, [&](bool activate){
        if(!activate)
        {
            close();
        }
    });
    QDBusInterface *iface = new QDBusInterface("org.freedesktop.login1",
                                               "/org/freedesktop/login1",
                                               "org.freedesktop.login1.Manager",
                                               QDBusConnection::systemBus(),
                                               this);
    connect(iface, SIGNAL(PrepareForSleep(bool)), this, SLOT(onPrepareForSleep(bool)));

    QDBusInterface *interfaceScreensaver = new QDBusInterface(
                SS_DBUS_SERVICE,
                SS_DBUS_PATH,
                SS_DBUS_INTERFACE,
                QDBusConnection::sessionBus(),
                this);

    connect(interfaceScreensaver, SIGNAL(lock()),
            this, SLOT(onSysLock()));
    connect(interfaceScreensaver, SIGNAL(unlock()),
            this, SLOT(onSysUnLock()));
}

QRCodeEnrollDialog::~QRCodeEnrollDialog()
{
    delete ui;
}

void QRCodeEnrollDialog::setupInit()
{
    setWindowTitle(tr(""));
//    setWindowFlags(Qt::FramelessWindowHint | Qt::Tool);
//    setAttribute(Qt::WA_TranslucentBackground);
    setAttribute(Qt::WA_DeleteOnClose);

    ui->closeBtn->setIcon(QIcon::fromTheme("window-close-symbolic"));
    ui->closeBtn->setAttribute(Qt::WA_NoMousePropagation);
    ui->closeBtn->setProperty("isWindowButton", 0x2);
    ui->closeBtn->setProperty("useIconHighlightEffect", 0x8);
    ui->closeBtn->setFixedSize(30, 30);

    QVBoxLayout *layoutImg = new QVBoxLayout(ui->qrCodeImageLabel);
    layoutImg->setContentsMargins(0,0,0,0);
    layoutImg->setSpacing(8);
    layoutImg->setAlignment(Qt::AlignCenter);
    m_labelTipMsg = new QLabel();
    m_labelTipMsg->setAlignment(Qt::AlignCenter);
    m_labelTipMsg->setWordWrap(true);
    layoutImg->addWidget(m_labelTipMsg);
    m_labelTipMsgS = new KALabel();
    layoutImg->addWidget(m_labelTipMsgS);
    m_labelTipMsgE = new QLabel();
    QPalette pe;
    pe.setColor(QPalette::WindowText,Qt::red);
    m_labelTipMsgE->setPalette(pe);
    layoutImg->addWidget(m_labelTipMsgE);
    //m_labelTipMsg->hide();
    m_labelTipMsgS->hide();
    m_labelTipMsgE->hide();

    if(!w_timer)
    {
        w_timer = new QTimer(this);
        w_timer->setInterval(150);
        connect(w_timer, &QTimer::timeout, this, &QRCodeEnrollDialog::updatePixmap);
    }
    m_waitingPixmap = QIcon::fromTheme("ukui-loading-0-symbolic").pixmap(24, 24);
    m_labelTipMsg->setPixmap(m_waitingPixmap);
    m_labelTipMsg->show();
    w_timer->start();
}

void QRCodeEnrollDialog::setTitle(int biotype)
{
    QString title = "";
    switch(biotype) {
    case ENROLL:
        title += tr("Bind Wechat Account");
        break;
    case VERIFY:
        title += tr("Verify Wechat Account");
        break;
    case SEARCH:
        title += tr("Search Wechat Account");
        break;
    }

    ui->bindQRCodeTitle->setText(title);
}

void QRCodeEnrollDialog::setPrompt(QString text)
{
    if (text.isEmpty()) {
        ui->bindQRCodeTip->setText("");
        ui->bindQRCodeTip->hide();
    } else {

        ui->bindQRCodeTip->setText(text);
        ui->bindQRCodeTip->show();
    }
}

void QRCodeEnrollDialog::on_closeBtn_clicked()
{
    close();
}

QRCodeEnrollDialog::Result QRCodeEnrollDialog::getResult()
{
    return opsResult;
}

int QRCodeEnrollDialog::enroll(int drvId, int u_id, int i_dx, const QString &idx_Name)
{
    if (ops == ENROLL) {
        return 1;
    }
    //m_labelTipMsg->hide();
    m_labelTipMsgS->hide();
    m_labelTipMsgE->hide();
    ui->confirmPushBtn->hide();
    QList<QVariant> args;
    args << drvId << uid << idx << idxName;
    deviceId = drvId;
    uid = u_id;
    idx = i_dx;
    idxName = idx_Name;
    this->setTitle(ENROLL);
    this->setPrompt(tr("Please use wechat scanning code for binding."));
    qDebug()<<"args:"<<args;
    serviceInterface->callWithCallback("Enroll", args, this,
                        SLOT(enrollCallBack(const QDBusMessage &)),
                        SLOT(errorCallBack(const QDBusError &)));
    ops = ENROLL;
    m_hisOps = ENROLL;
    m_listEnrollArgs = args;

    return 0;
}

void QRCodeEnrollDialog::enrollCallBack(const QDBusMessage &reply)
{
    int result;
    dup_fd = -1;
    result = reply.arguments()[0].value<int>();
    qDebug() << "Enroll result: " << result;

    switch(result) {
    case DBUS_RESULT_SUCCESS: { /* 录入成功 */
        opsResult = SUCESS;
        setPrompt(tr("Bind Successfully"));
        showFinishPrompt();
        break;
    }
    default:
        opsResult = ERROR;
        handleErrorResult(result);
        break;
    }
    ops = IDLE;
}

int QRCodeEnrollDialog::verify(int drvId, int uid, int idx)
{
    QList<QVariant> args;
    args << drvId << uid << idx;

    this->setTitle(VERIFY);
    serviceInterface->callWithCallback("Verify", args, this,
                        SLOT(verifyCallBack(const QDBusMessage &)),
                        SLOT(errorCallBack(const QDBusError &)));
    ops = VERIFY;

    return exec();
}

void QRCodeEnrollDialog::verifyCallBack(const QDBusMessage &reply)
{
    int result;
    dup_fd = -1;
    result = reply.arguments()[0].value<int>();
    qDebug() << "Verify result: " << result;

    if(result >= 0) {
        opsResult = SUCESS;
        setPrompt(tr("Verify successfully"));
        showFinishPrompt();
    } else if(result == DBUS_RESULT_NOTMATCH) {
        ui->qrCodeImageLabel->setStyleSheet("background-color:#FFFFFF");
        //setPrompt(tr("Not Match"));
        m_labelTipMsg->setPixmap(QIcon::fromTheme("dialog-error").pixmap(QSize(64,64)));
        ui->confirmPushBtn->show();
        m_labelTipMsgE->setText(tr("Not Match"));
        m_labelTipMsgE->show();
	//showFinishPrompt();
    } else {
        handleErrorResult(result);
    }

    ops = IDLE;
}

QStringList QRCodeEnrollDialog::getFeaturelist(int drvid, int uid, int indexStart, int indexEnd)
{
    QStringList list;
    QList<QDBusVariant> qlist;
    FeatureInfo *featureInfo;
    int listsize;
    QDBusMessage result = serviceInterface->call(QStringLiteral("GetFeatureList"),drvid,uid,indexStart,indexEnd);
    if(result.type() == QDBusMessage::ErrorMessage)
    {
        qWarning() << "GetDevList error:" << result.errorMessage();
        return list;
    }
    QList<QVariant> variantList = result.arguments();
    listsize = variantList[0].value<int>();
    variantList[1].value<QDBusArgument>() >> qlist;
    for (int i = 0; i < listsize; i++) {
        featureInfo = new FeatureInfo;
        qlist[i].variant().value<QDBusArgument>() >> *featureInfo;
        list.append(featureInfo->index_name);
        delete featureInfo;
    }
    return list;
}

int QRCodeEnrollDialog::search(int drvId, int uid, int idxStart, int idxEnd)
{
    QList<QVariant> args;
    args << drvId << uid << idxStart << idxEnd;

    this->setTitle(SEARCH);

    serviceInterface->callWithCallback("Search", args, this,
                        SLOT(searchCallBack(const QDBusMessage &)),
                        SLOT(errorCallBack(const QDBusError &)));

    ops = SEARCH;

    return exec();
}

void QRCodeEnrollDialog::searchCallBack(const QDBusMessage &reply)
{
    return ;
}


void QRCodeEnrollDialog::showFinishPrompt()
{
    if(w_timer && w_timer->isActive())
    {
        w_timer->stop();
    }
    m_labelTipMsg->setPixmap(QIcon::fromTheme("dialog-success").pixmap(QSize(64,64)));
    ui->confirmPushBtn->show();
    ui->bindQRCodeTip->setText("");
    ui->bindQRCodeTitle->setText("");
    ui->qrCodeImageLabel->clear();
    ui->qrCodeImageLabel->setStyleSheet("");
    m_labelTipMsgE->hide();
    m_labelTipMsg->show();
    m_labelTipMsgS->show();
    if(ops == ENROLL){
        m_labelTipMsgS->setText(tr("The wechat account is bound successfully!"));
    }
    else if(ops == VERIFY){
        m_labelTipMsgS->setText(tr("Verify successfully"));
    }
//    QTimer::singleShot(1000, this, [&,this](){
//        this->on_closeBtn_clicked();
//    });
}

void QRCodeEnrollDialog::StopOpsCallBack(const QDBusMessage &reply)
{
    dup_fd = -1;
    int ret = reply.arguments().at(0).toInt();
    accept();
}

void QRCodeEnrollDialog::errorCallBack(const QDBusError &error)
{
    dup_fd = -1;
    qDebug() << "DBus Error: " << error.message();
    accept();
}

void QRCodeEnrollDialog::closeEvent(QCloseEvent *event)
{
    serviceInterface->call("StopOps", deviceId, 1);
}

void QRCodeEnrollDialog::onFrameWritten(int drvId)
{
    if(w_timer && w_timer->isActive())
    {
        w_timer->stop();
    }
    m_labelTipMsg->hide();
    if (ops == IDLE) {
        qDebug()<<"Not in enroll or verify!";
        return ;
    }
    if (drvId != deviceId) {
        qDebug()<<"Device not pair!";
        return ;
    }
    if(dup_fd == -1){
        dup_fd = get_server_gvariant_stdout(drvId);
    }

    if(dup_fd < 0)
        return ;

    cv::Mat img;
    lseek(dup_fd, 0, SEEK_SET);
    char base64_bufferData[1024*1024];
    int rc = read(dup_fd, base64_bufferData, 1024*1024);
    printf("rc = %d\n", rc);

    cv::Mat mat2(1, sizeof(base64_bufferData), CV_8U, base64_bufferData);
    img = cv::imdecode(mat2, cv::IMREAD_COLOR);
    qDebug()<<"img:"<<img.cols<<","<<img.rows;
    if (img.cols <= 0 || img.rows <= 0) {
        QFileInfo fileInfoImg("/var/lib/biometric-auth/qr_image.jpg");
        if (fileInfoImg.exists()) {
            QImage srcQImage("/var/lib/biometric-auth/qr_image.jpg");
            ui->qrCodeImageLabel->setPixmap(QPixmap::fromImage(srcQImage).scaled(QSize(200,200)));
            //setPrompt(tr("Please use wechat scanning code for binding."));
        }
    } else {
        QImage srcQImage = QImage((uchar*)(img.data), img.cols, img.rows, QImage::Format_RGB888);
        ui->qrCodeImageLabel->setPixmap(QPixmap::fromImage(srcQImage).scaled(QSize(200,200)));
        //setPrompt(tr("Please use wechat scanning code for binding."));
    }

    qDebug()<<"onFrameWritten end!!";
}


void QRCodeEnrollDialog::onProcessChanged(int drvId,QString  aa, int statusType,QString bb)
{
    int count = statusType * 15 / 100;
    QString filename = QString("/usr/share/ukui-biometric/images/huawei/") + (count < 10 ? "0" : "") +
            QString::number(count) + ".svg";

    qDebug()<<"filename:"<<filename;
}

void QRCodeEnrollDialog::onStatusChanged(int drvId, int statusType)
{
    if (!(drvId == deviceId && statusType == STATUS_NOTIFY))
        return;
    qDebug()<<"onStatusChanged:"<<drvId<<","<<statusType;

    //过滤掉当录入时使用生物识别授权接收到的认证的提示信息
    if(ops == ENROLL) {
        QDBusMessage reply = serviceInterface->call("UpdateStatus", drvId);
        if(reply.type() == QDBusMessage::ErrorMessage) {
            qDebug() << "DBUS: " << reply.errorMessage();
            return;
        }
        int devStatus = reply.arguments().at(3).toInt();
        qDebug()<<"~~~~~~~~~~~~~~~~~~statusType="<<statusType<<"devStatus"<<devStatus;
        if(!(devStatus >= 201 && devStatus < 203)) {
            return;
        }
        //认证结束后，重新获取fd，刷新录入界面
        if(devStatus == 201 && statusType == STATUS_NOTIFY) {
            dup_fd = -1;
        }
    }
    else if(ops == IDLE)
    {
        return;
    }

    QDBusMessage notifyReply = serviceInterface->call("GetNotifyMesg", drvId);
    if(notifyReply.type() == QDBusMessage::ErrorMessage) {
        qDebug() << "DBUS: " << notifyReply.errorMessage();
        return;
    }
    QString prompt = notifyReply.arguments().at(0).toString();
    qDebug() << prompt;
    //setPrompt(prompt);
}

void QRCodeEnrollDialog::on_confirmPushBtn_clicked()
{
    close();
}

void QRCodeEnrollDialog::handleErrorResult(int error)
{
    qDebug()<<"error = "<<error;
    ui->qrCodeImageLabel->setStyleSheet("background-color:#FFFFFF");
    ui->qrCodeImageLabel->setPixmap(QPixmap(":/imgs/resources/ukui-qrcode-null.svg"));
    //m_labelTipMsg->hide();
    //m_labelTipMsgE->hide();
    m_labelTipMsgS->hide();
    dup_fd = -1;
    if(w_timer && w_timer->isActive())
    {
        w_timer->stop();
    }
    m_labelTipMsg->setPixmap(QIcon::fromTheme("dialog-warning").pixmap(QSize(22,22)));
    m_labelTipMsgE->show();
    switch(error) {
    case DBUS_RESULT_ERROR: {
        //操作失败，需要进一步获取失败原因
        QDBusMessage msg = serviceInterface->call("GetOpsMesg", deviceId);
        if(msg.type() == QDBusMessage::ErrorMessage)
        {
            qDebug() << "UpdateStatus error: " << msg.errorMessage();
            //setPrompt(tr("D-Bus calling error"));
            return;
        }

        statusReslut ret = UpdateStatus(deviceId);
        qDebug()<<"StatusReslut:"<<ret.result<<","<<ret.enable<<","<<ret.devNum<<","
                  <<ret.devStatus<<","<<ret.opsStatus<<","<<ret.notifyMessageId;

        if(ret.opsStatus == 8){
            //m_labelTipMsg->setPixmap(QIcon::fromTheme("dialog-warning").pixmap(QSize(22,22)));
            ui->bindQRCodeTip->show();
            ui->bindQRCodeTitle->show();
            ui->confirmPushBtn->show();
            m_labelTipMsg->show();
            m_labelTipMsgE->show();
            m_labelTipMsgS->hide();
            m_labelTipMsgE->setText(tr("Abnormal network"));
            QTimer::singleShot(10000, this, [&,this](){
                enroll(deviceId,uid,idx,idxName);
            });
        } else {
            QTimer::singleShot(5000, this, [&,this](){
                enroll(deviceId,uid,idx,idxName);
            });
        }
        break;
    }
    case DBUS_RESULT_DEVICEBUSY:
        //设备忙
        //setPrompt(tr("Binding failure"));
        m_labelTipMsgE->setText(tr("Binding failure"));
        m_hisOps = IDLE;
        break;
    case DBUS_RESULT_NOSUCHDEVICE:
        //设备不存在
        //setPrompt(tr("Binding failure"));
        m_labelTipMsgE->setText(tr("Binding failure"));
        m_hisOps = IDLE;
        break;
    case DBUS_RESULT_PERMISSIONDENIED:
        //没有权限
        //setPrompt(tr("Binding failure"));
        m_labelTipMsgE->setText(tr("Binding failure"));
        m_hisOps = IDLE;
        break;
    }

}

statusReslut QRCodeEnrollDialog::UpdateStatus(int drvid)
{
    statusReslut status;
    QDBusMessage result = serviceInterface->call(QStringLiteral("UpdateStatus"), drvid);
    if(result.type() == QDBusMessage::ErrorMessage)
    {
        qWarning() << "UpdateStatus error:" << result.errorMessage();
        status.result = -1;
        return status;
    }

    status.result           = result.arguments().at(0).toInt();
    status.enable           = result.arguments().at(1).toInt();
    status.devNum           = result.arguments().at(2).toInt();
    status.devStatus        = result.arguments().at(3).toInt();
    status.opsStatus        = result.arguments().at(4).toInt();
    status.notifyMessageId  = result.arguments().at(5).toInt();

    return status;
}

void QRCodeEnrollDialog::paintEvent(QPaintEvent * event){
    Q_UNUSED(event)

//    QPainter p(this);
//    p.setRenderHint(QPainter::Antialiasing);
//    QPainterPath rectPath;
//    rectPath.addRoundedRect(this->rect().adjusted(10, 10, -10, -10), 6, 6);

//    // 画一个黑底
//    QPixmap pixmap(this->rect().size());
//    pixmap.fill(Qt::transparent);
//    QPainter pixmapPainter(&pixmap);
//    pixmapPainter.setRenderHint(QPainter::Antialiasing);
//    pixmapPainter.setPen(Qt::transparent);
//    pixmapPainter.setBrush(Qt::black);
//    pixmapPainter.drawPath(rectPath);
//    pixmapPainter.end();

//    // 模糊这个黑底
//    QImage img = pixmap.toImage();
//    qt_blurImage(img, 10, false, false);

//    // 挖掉中心
//    pixmap = QPixmap::fromImage(img);
//    QPainter pixmapPainter2(&pixmap);
//    pixmapPainter2.setRenderHint(QPainter::Antialiasing);
//    pixmapPainter2.setCompositionMode(QPainter::CompositionMode_Clear);
//    pixmapPainter2.setPen(Qt::transparent);
//    pixmapPainter2.setBrush(Qt::transparent);
//    pixmapPainter2.drawPath(rectPath);

//    // 绘制阴影
//    p.drawPixmap(this->rect(), pixmap, pixmap.rect());

//    // 绘制一个背景
//    p.save();
//    p.fillPath(rectPath,palette().color(QPalette::Base));
//    p.restore();
}

void QRCodeEnrollDialog::onPrepareForSleep(bool sleep)
{
    ///系统休眠时，会关闭总线，导致设备不可用，发生错误
    ///在系统休眠之前停止认证，在系统唤醒后重新开始认证
    bool isChanged = false;
    if(sleep) {
        if (!(m_uSysState&SYSSTATE_SLEEP)) {
            m_uSysState |= SYSSTATE_SLEEP;
            isChanged = true;
        }
    } else {
        if (m_uSysState&SYSSTATE_SLEEP) {
            m_uSysState &= ~SYSSTATE_SLEEP;
            isChanged = true;
        }
    }
    if (isChanged) {
        chkSysState();
    }
}

void QRCodeEnrollDialog::onSysLock()
{
    if (!(m_uSysState&SYSSTATE_LOCK)) {
        m_uSysState |= SYSSTATE_LOCK;
        chkSysState();
    }
}

void QRCodeEnrollDialog::onSysUnLock()
{
    if (m_uSysState&SYSSTATE_LOCK) {
        m_uSysState &= ~SYSSTATE_LOCK;
        chkSysState();
    }
}

void QRCodeEnrollDialog::chkSysState()
{
    qInfo()<<"chkSysState:"<<m_uSysState<<","<<m_hisOps;
    if (m_uSysState) {  // 停止
        serviceInterface->call("StopOps", deviceId, 1);
    } else { // 恢复
        if(m_hisOps == IDLE)
            return;
        setPrompt(tr("Please use wechat scanning code for binding."));
        switch(m_hisOps) {
        case ENROLL:
        {
            this->serviceInterface->callWithCallback("Enroll", this->m_listEnrollArgs, this,
                                SLOT(enrollCallBack(const QDBusMessage &)),
                                SLOT(errorCallBack(const QDBusError &)));
            this->ops = this->m_hisOps;
        }
            break;
         default:
            break;
        }
    }
}

void QRCodeEnrollDialog::updatePixmap()
{
    QMatrix matrix;
    matrix.rotate(90.0);
    m_waitingPixmap = m_waitingPixmap.transformed(matrix, Qt::FastTransformation);
    m_labelTipMsg->setPixmap(m_waitingPixmap);
    m_labelTipMsg->show();
}
